
#include"glheader.h"
#include"sphere.h"
#include"box.h"
#include"mesh.h"
#include"raytracer.h"
#include"raycaster.h"
#include"light.h"
#include"vmatrix.h"

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>

#define WW 800
//#define WW 300
#define WH WW

#define MULTI_THREAD

static bool bg = false;
static float * img;
static int img_h;
static int img_w;
static Sphere * sphere;
static Sphere * sphere2;
static Box * box;
//static Box * box2;
static RayCaster * raycaster;
static RayTracer * raytracer;
static Light * light;
static Mesh * mesh;
static VMatrix* mstack;

pthread_t thread1, thread2;

#define checkImageWidth 128
#define checkImageHeight 128
float checkImage[checkImageHeight][checkImageWidth][3];
float otherImage[checkImageHeight][checkImageWidth][3];

void makeCheckImages(void)
{
   int i, j;
   float c;
    
   for (i = 0; i < checkImageHeight; i++) {
      for (j = 0; j < checkImageWidth; j++) {
         c = (float)((((i&0x8)==0)^((j&0x8))==0));
         checkImage[i][j][0] = c;
         checkImage[i][j][1] = 1.0;
         checkImage[i][j][2] = c;
         //checkImage[i][j][3] = 255;
         c = ((((i&0x10)==0)^((j&0x10))==0))*255;

         otherImage[i][j][0] = c;
         otherImage[i][j][1] = 0;
         otherImage[i][j][2] = 0;
         //otherImage[i][j][3] = (GLubyte) 255;
      }
   }
}

void Redraw ()
{
  //printf("Redraw\n");
  if(bg)
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 
  else
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT);
  //glPixelZoom(8.0,8.0);
  glWindowPos2i(0,0);
  glDrawPixels(img_w,img_h, GL_RGB,GL_FLOAT, img);
  glutSwapBuffers();
}

int r1[4];
int r2[4];
bool aa = false;

void *thread_func (void* ptr)
{
  int * r = (int *) ptr;
  raytracer->Execute(r[0], r[1], r[2], r[3], img, img_w, img_h,aa);
}

void Retrace (bool clear)
{
  if(clear){
    for(int x=0; x<img_w; x++){
      for(int y=0; y<img_h; y++){
        float * c = &img[(img_w*y+x)*3];
        c[0] = 0.0;
        c[1] = 0.0;
        c[2] = 0.0;
      }
    }
  }
#ifdef MULTI_THREAD
  r1[0]=0; r1[1]=img_w/2; r1[2]=0; r1[3]=img_h-1;
  r2[0]=img_w/2+1; r2[1]=img_w-1; r2[2]=0; r2[3]=img_h-1;
  pthread_create( &thread1, NULL, thread_func, (void*) r1);
  pthread_create( &thread2, NULL, thread_func, (void*) r2);
  //pthread_join( thread1, NULL);
  //pthread_join( thread2, NULL);
#else
  raytracer->Execute(0.0, img_w-1, 0.0, img_h-1, img, img_w, img_h,true);
#endif
}

void Reshape(int w, int h)
{
  if(img!=NULL)
    free(img);
  img_w = w;
  img_h = h;
  img = (float*) calloc(w*h*3,sizeof(float));
  raycaster->ChangeNs(w,h);
  Retrace(false);
}

void Idle ()
{
  usleep(300);
  Redraw();
}


#define MESH___
#define SPHERE___
#define BOX___

float sup = 0.0;
float bup = -0.30;

void RotateWorld (float angle)
{
  float mat[16];
  mstack->LoadIdentity();
  mstack->LookAt(0.0, 0.50, 5.0,
                  0.0, 0.0, 0.0,
                  0.0f, 1.0f, 0.0f);
  //mstack->Translate(0.0, -2.00, -5.0);
  mstack->Rotate(angle, 0.0, 1.0, 0.0);
  mstack->GetMatrix(mat);
  light->SetViewPos(mat);
#ifdef BOX___
  mstack->GetMatrixInverse(box->GetMInvPointer());
  mstack->GetMatrixIT(box->GetMITPointer());
#endif
#ifdef SPHERE___
  mstack->Push();
  mstack->Translate(0.0, sup, 0.0);
  mstack->GetMatrix(mat);
  sphere->SetViewPos(mat);
  mstack->GetMatrixInverse(sphere->GetMInvPointer());
  mstack->GetMatrixIT(sphere->GetMITPointer());
  mstack->Pop();
#endif
#ifdef MESH___
  float sc = 11.0;
  mstack->Translate(0.0, bup, 0.0);
  mstack->Scale(sc,sc,sc);
  mstack->GetMatrixInverse(mesh->GetMInvPointer());
  mstack->GetMatrixIT(mesh->GetMITPointer());
#endif
}

void Keyboard(unsigned char key, int x, int y)
{
  static float angle = 20.0;
	switch ( key )
	{
    case 'a':
      aa=!aa;
      break;
    case 'r':
      Retrace(true);
      glutPostRedisplay();
      return;
      break;
    case 's':
      sup+=0.2;
      break;
    case 'S':
      sup-=0.2;
      break;
    case 'b':
      bup+=0.2;
      break;
    case 'B':
      bup-=0.2;
      break;
    case ']':
      angle+=10;
      break;
    case '[':
      angle-=10;
      break;
		case 27:
		case 'q':
		case 'Q':
			exit(0);
	}
  RotateWorld(angle);
  Retrace(true);
  glutPostRedisplay();
}

static void Init ()
{
  GLenum err = glewInit();
  if (GLEW_OK != err) { 
    fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
    exit(-1);
  }
  //glEnable(GL_DEPTH_TEST);
  img = NULL;
  raycaster = new RayCaster(45.0, 1.0, WW, WH,5);
  raytracer = new RayTracer(raycaster, 2);
  float zballs = -6.0;
  float shine = 50.0;
#ifdef SPHERE___
  sphere = new Sphere(0.0, 0.20, 1.7, .30);
  sphere->SetColor(0.0,0.0,0.70, shine,0.0);
  raytracer->InsertObj(sphere);
#endif
#ifdef MESH___
  mesh = new Mesh(3);
  mesh->ShapeFromFile(BUNNY);
  //mesh->ShapeFromFile(DRAG);
  //mesh->ShapeSphere(50);
  //mesh->ShapeTorus(50);
  mesh->SetColor(1.0,0.1,0.1,shine,0.2);
  raytracer->InsertObj(mesh);
#endif
#ifdef BOX___
  makeCheckImages();
  float x0=-2.5; float y0=-0.5; float z0=-3.0;
  box = new Box(x0, x0+5, y0,y0+0.2, z0,z0+6.0);
  box->SetColor(0.0,1.0,1.0, -1.0,0.3);
  box->SetImage((float*)checkImage,checkImageWidth,checkImageWidth);
  raytracer->InsertObj(box);
  x0=-1.5; y0=0.3; z0=-5.070;
#endif
  light = new Light(-1.0, 15.0, zballs+2);
  raytracer->InsertLight(light);
  Light* light2 = new Light(0.0, 0.0, 0.0);
  //light = new Light(1.0, 15.0, zballs+2);
  raytracer->InsertLight(light2);
  raytracer->SetAmb(0.2);
  mstack = new VMatrix();
  RotateWorld(20.0);
}

int main (int argc, char** argv)
{
  //printf("%s\n",argv[0]);
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
  glutInitWindowSize(WW,WH);
  glutCreateWindow("Ray-Tracer-Test");
  glutDisplayFunc(Redraw);
  glutKeyboardFunc(Keyboard);
  glutReshapeFunc(Reshape);
  glutIdleFunc(Idle);
  Init();
  glutMainLoop();
  return 0;
}

